/*
 * This file is part of Ext JS 3.4 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage This file may be
 * used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in
 * the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met:
 * http://www.gnu.org/copyleft/gpl.html. If you are unsure which license is appropriate for your use, please contact the sales department at
 * http://www.sencha.com/contact. Build date: 2013-04-03 15:07:25
 */
/**
 * @class Ext.data.HttpProxy
 * @extends Ext.data.DataProxy <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same domain of the originating page.</p>
 *          <p><b>Note</b>: this class cannot be used to retrieve data from a domain other than the domain from which the running page was served. For
 *          cross-domain requests, use a {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p> <p>Be aware that to enable the browser to parse an XML document,
 *          the server must set the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>
 * @constructor
 * @param {Object} conn An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}. <p>Note that if this HttpProxy is being used
 *        by a {@link Ext.data.Store Store}, then the Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>
 *        options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters, or react to loading events. The Store's
 *        {@link Ext.data.Store#baseParams baseParams} may also be used to pass parameters known at instantiation time.</p> <p>If an options parameter is
 *        passed, the singleton {@link Ext.Ajax} object will be used to make the request.</p>
 */
Ext.data.HttpProxy = function( conn )
{
	Ext.data.HttpProxy.superclass.constructor.call(this, conn);

	/**
	 * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy uses to make requests to the server. Properties of this
	 * object may be changed dynamically to change the way data is requested.
	 * 
	 * @property
	 */
	this.conn = conn;

	// nullify the connection url. The url param has been copied to 'this' above. The connection
	// url will be set during each execution of doRequest when buildUrl is called. This makes it easier for users to override the
	// connection url during beforeaction events (ie: beforeload, beforewrite, etc).
	// Url is always re-defined during doRequest.
	this.conn.url = null;

	this.useAjax = !conn || !conn.events;

	// A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]
	var actions = Ext.data.Api.actions;
	this.activeRequest = {};
	for( var verb in actions )
	{
		this.activeRequest[actions[verb]] = undefined;
	}
};

Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy,
{
	/**
	 * Return the {@link Ext.data.Connection} object being used by this Proxy.
	 * 
	 * @return {Connection} The Connection object. This object may be used to subscribe to events on a finer-grained basis than the DataProxy events.
	 */
	getConnection: function()
	{
		return this.useAjax ? Ext.Ajax : this.conn;
	},

	/**
	 * Used for overriding the url used for a single request. Designed to be called during a beforeaction event. Calling setUrl will override any urls set via the
	 * api configuration parameter. Set the optional parameter makePermanent to set the url for all subsequent requests. If not set to makePermanent, the next
	 * request will use the same url or api configuration defined in the initial proxy configuration.
	 * 
	 * @param {String} url
	 * @param {Boolean} makePermanent (Optional) [false] (e.g.: beforeload, beforesave, etc).
	 */
	setUrl: function( url, makePermanent )
	{
		this.conn.url = url;
		if( makePermanent === true )
		{
			this.url = url;
			this.api = null;
			Ext.data.Api.prepare(this);
		}
	},

	// 集成于父类的模板方法
	// this.proxy.request('read',null,{username:'abc',age:11},this.reader,fn,this,{params:{sort:'age',dir:'desc'},baseParams:{xxxx},params:{XXXXXXXXX}}});
	doRequest: function( action, rs, params, reader, cb, scope, arg )
	{
		var o =
		{
			method: (this.api[action]) ? this.api[action]['method'] : undefined,
			request:
			{
				callback: cb,
				scope: scope,
				arg: arg
			},
			reader: reader,
			callback: this.createCallback(action, rs), // 请求数据之后的回调函数
			scope: this
		};

		var o =
		{
			method: 'get',
			request:
			{
				callback: function()
				{
				},
				scope: this,
				args: null
			},
			reader: reader,
			callback: function()
			{
			},
			scope: this
		};

		// If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).
		// Use std HTTP params otherwise.
		if( params.jsonData )
		{
			o.jsonData = params.jsonData;
		}
		else if( params.xmlData )
		{
			o.xmlData = params.xmlData;
		}
		else
		{
			o.params = params || {};
		}
		// Set the connection url. If this.conn.url is not null here,
		// the user must have overridden the url during a beforewrite/beforeload event-handler.
		// this.conn.url is nullified after each request.
		this.conn.url = this.buildUrl(action, rs);

		if( this.useAjax )
		{

			Ext.applyIf(o, this.conn);

			// If a currently running read request is found, abort it
			if( action == Ext.data.Api.actions.read && this.activeRequest[action] )
			{
				Ext.Ajax.abort(this.activeRequest[action]);
			}

			// 这里才是正在的ajax请求
			this.activeRequest[action] = Ext.Ajax.request(o);
		}
		else
		{
			this.conn.request(o);
		}
		// request is sent, nullify the connection url in preparation for the next request
		this.conn.url = null;
	},

	/**
	 * Returns a callback function for a request. Note a special case is made for the read action vs all the others.
	 * 
	 * @param {String} action [create|update|delete|load]
	 * @param {Ext.data.Record[]} rs The Store-recordset being acted upon
	 * @private
	 */
	createCallback: function( action, rs )
	{
		return function( o, success, response )
		{
			this.activeRequest[action] = undefined;
			if( !success )
			{
				if( action === Ext.data.Api.actions.read )
				{
					// @deprecated: fire loadexception for backwards compat.
					// TODO remove
					this.fireEvent('loadexception', this, o, response);
				}
				this.fireEvent('exception', this, 'response', action, o, response);
				o.request.callback.call(o.request.scope, null, o.request.arg, false);
				return;
			}
			if( action === Ext.data.Api.actions.read )
			{
				this.onRead(action, o, response);
			}
			else
			{
				this.onWrite(action, o, response, rs);
			}
		};
	},

	/**
	 * Callback for read action
	 * 
	 * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
	 * @param {Object} o The request transaction object
	 * @param {Object} res The server response
	 * @fires loadexception (deprecated)
	 * @fires exception
	 * @fires load
	 * @protected
	 */
	onRead: function( action, o, response )
	{
		var result;
		try
		{
			// 调用数据解析器进行数据的解析
			result = o.reader.read(response);
		}
		catch (e)
		{
			// @deprecated: fire old loadexception for backwards-compat.
			// TODO remove
			this.fireEvent('loadexception', this, o, response, e);

			this.fireEvent('exception', this, 'response', action, o, response, e);
			o.request.callback.call(o.request.scope, null, o.request.arg, false);
			return;
		}
		if( result.success === false ) // 解析器没有解析成功
		{
			// @deprecated: fire old loadexception for backwards-compat.
			// TODO remove
			this.fireEvent('loadexception', this, o, response);

			// Get DataReader read-back a response-object to pass along to exception event
			var res = o.reader.readResponse(action, response);
			this.fireEvent('exception', this, 'remote', action, o, res, null);
		}
		else
		{
			// 解析成功，触发load事件
			this.fireEvent('load', this, o, o.request.arg);
		}
		// TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
		// the calls to request.callback(...) in each will have to be made identical.
		// NOTE reader.readResponse does not currently return Ext.data.Response
		o.request.callback.call(o.request.scope, result, o.request.arg, result.success);
	},
	/**
	 * Callback for write actions
	 * 
	 * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
	 * @param {Object} trans The request transaction object
	 * @param {Object} res The server response
	 * @fires exception
	 * @fires write
	 * @protected
	 */
	onWrite: function( action, o, response, rs )
	{
		var reader = o.reader;
		var res;
		try
		{
			res = reader.readResponse(action, response);
		}
		catch (e)
		{
			this.fireEvent('exception', this, 'response', action, o, response, e);
			o.request.callback.call(o.request.scope, null, o.request.arg, false);
			return;
		}
		if( res.success === true )
		{
			this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);
		}
		else
		{
			this.fireEvent('exception', this, 'remote', action, o, res, rs);
		}
		// TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
		// the calls to request.callback(...) in each will have to be made similar.
		// NOTE reader.readResponse does not currently return Ext.data.Response
		o.request.callback.call(o.request.scope, res.data, res, res.success);
	},

	// inherit docs
	destroy: function()
	{
		if( !this.useAjax )
		{
			this.conn.abort();
		}
		else if( this.activeRequest )
		{
			var actions = Ext.data.Api.actions;
			for( var verb in actions )
			{
				if( this.activeRequest[actions[verb]] )
				{
					Ext.Ajax.abort(this.activeRequest[actions[verb]]);
				}
			}
		}
		Ext.data.HttpProxy.superclass.destroy.call(this);
	}
});